package cmd

import (
	"AccessAdmin/api/frontend"
	"AccessAdmin/internal/consts"
	"AccessAdmin/internal/dao"
	"AccessAdmin/internal/model/entity"
	"AccessAdmin/utility/response"
	"AccessAdmin/utility/wechat"
	"context"
	"database/sql"
	"github.com/goflyfox/gtoken/gtoken"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/net/ghttp"
	"github.com/gogf/gf/v2/text/gstr"
	"github.com/gogf/gf/v2/util/gconv"
	"strconv"
)

// 前台登录gtoken相关
func StartFrontendGToken() (gfFrontendToken *gtoken.GfToken, err error) {
	gfFrontendToken = &gtoken.GfToken{
		CacheMode:        consts.CacheModeRedis,
		ServerName:       consts.FrontendServerName,
		LoginPath:        "/user/login",
		LoginBeforeFunc:  loginFuncFrontend,
		LoginAfterFunc:   loginAfterFuncFrontend,
		LogoutPath:       "/user/logout",
		AuthAfterFunc:    authAfterFuncFrontend,
		MultiLogin:       consts.FrontendMultiLogin,
		AuthExcludePaths: g.SliceStr{"/api/user/list"},
	}
	//todo 去掉全局校验，只用cmd中的路由组校验
	//err = gfAdminToken.Start()
	return
}

// 微信登录 前台项目
func loginFuncFrontend(r *ghttp.Request) (string, interface{}) {
	code := r.Get("code").String()
	ctx := context.TODO()

	if code == "" {
		r.Response.WriteJson(gtoken.Fail(consts.ErrLoginFaultMsg))
		r.ExitAll()
	}

	session, err := wechat.GetMiniProgramInstance().Auth.Session(ctx, code)
	if err != nil {
		r.Response.WriteJson(gtoken.Fail(consts.ErrLoginFaultMsg))
		r.ExitAll()
	}

	//验证账号密码是否正确
	userInfo := &entity.UserApi{}
	err = dao.UserApi.Ctx(ctx).Where(dao.UserApi.Columns().OpenId, session.OpenID).Scan(&userInfo)
	if err != nil && err.Error() != sql.ErrNoRows.Error() {
		r.Response.WriteJson(gtoken.Fail(consts.ErrLoginFaultMsg))
		r.ExitAll()
	}
	if err != nil && err.Error() == sql.ErrNoRows.Error() {
		var uid int64
		uid, err = dao.UserApi.Ctx(ctx).Data(g.Map{
			dao.UserApi.Columns().OpenId:      session.OpenID,
			dao.UserApi.Columns().SessionKey:  session.SessionKey,
			dao.UserApi.Columns().UnionId:     session.UnionID,
			dao.UserApi.Columns().Status:      consts.StatusEnable,
			dao.UserApi.Columns().IsNeedGrant: consts.StatusEnable,
		}).InsertAndGetId()

		if err != nil {
			r.Response.WriteJson(gtoken.Fail(consts.ErrLoginFaultMsg))
			r.ExitAll()
		}

		_ = dao.UserApi.Ctx(ctx).Where(dao.UserApi.Columns().Id, uid).Scan(&userInfo)

	} else {
		_, err = dao.UserApi.Ctx(ctx).Data(g.Map{
			dao.UserApi.Columns().SessionKey: session.SessionKey,
		}).FieldsEx(dao.UserApi.Columns().Id).Where(dao.Attachment.Columns().Id, userInfo.Id).Update()

		if err != nil {
			r.Response.WriteJson(gtoken.Fail(consts.ErrLoginFaultMsg))
			r.ExitAll()
		}
	}

	// 唯一标识，扩展参数user data
	return consts.GTokenFrontendPrefix + strconv.Itoa(int(userInfo.Id)), userInfo
}

// 自定义的登录之后的函数 for前台项目
func loginAfterFuncFrontend(r *ghttp.Request, respData gtoken.Resp) {
	if !respData.Success() {
		respData.Code = 0
		r.Response.WriteJson(respData)
		return
	} else {
		respData.Code = 1
		//获得登录用户id
		userKey := respData.GetString("userKey")
		userId := gstr.StrEx(userKey, consts.GTokenFrontendPrefix)
		//根据id获得登录用户其他信息
		userInfo := entity.UserApi{}
		err := dao.UserApi.Ctx(context.TODO()).WherePri(userId).Scan(&userInfo)
		if err != nil {
			return
		}
		data := &frontend.LoginDoRes{
			Type:     consts.TokenType,
			Token:    respData.GetString("token"),
			ExpireIn: consts.GTokenExpireIn, //单位秒,
		}
		response.JsonExit(r, 0, "", data)
	}
	return
}

// 登录鉴权中间件for前台
func authAfterFuncFrontend(r *ghttp.Request, respData gtoken.Resp) {
	var userInfo entity.UserApi
	err := gconv.Struct(respData.GetString("data"), &userInfo)
	if err != nil {
		response.AuthFail(r)
		return
	}
	//todo 这里可以写账号前置校验、是否被拉黑、有无权限等逻辑
	r.SetCtxVar(consts.CtxUserId, userInfo.Id)
	r.SetCtxVar(consts.CtxUserName, userInfo.NickName)
	r.SetCtxVar(consts.CtxUserAvatar, userInfo.AvatarUrl)
	r.SetCtxVar(consts.CtxUserStatus, userInfo.Status)
	r.Middleware.Next()
}
